package cn.mazexal.exambg.services;

import cn.hutool.core.util.IdUtil;
import cn.mazexal.exambg.common.models.PagedData;
import cn.mazexal.exambg.dataware.*;
import cn.mazexal.exambg.models.converter.*;
import cn.mazexal.exambg.models.db.*;
import cn.mazexal.exambg.models.qry.*;
import cn.mazexal.exambg.models.redisKeys.RedisTableIdKey;
import cn.mazexal.exambg.models.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author: Mazexal
 * Date: 2025/5/14
 */
@Service
public class CommonService {

    @Resource
    private RedisService redisService;

    @Resource
    private ConfigMapper configMapper;

    @Resource
    private ExamBankMapper examBankMapper;

    @Resource
    private CardMenuMapper cardMenuMapper;

    @Resource
    private ExamBankConverter examBankConverter;

    @Resource
    private CardMenuConverter cardMenuConverter;

    @Resource
    private TagConverter tagConverter;

    @Resource
    private TagMapper tagMapper;

    @Resource
    private UserFeedbackMapper userFeedbackMapper;

    @Resource
    private UserFeedbackConverter userFeedbackConverter;

    @Resource
    private VideoMenuMapper videoMenuMapper;

    @Resource
    private VideoMenuIntroMapper videoMenuIntroMapper;

    @Resource
    private VideoMenuConverter videoMenuConverter;
    @Resource
    private VideoMenuIntroConverter videoMenuIntroConverter;

    @Resource
    private SysChannelConverter sysChannelConverter;

    @Resource
    private SysChannelMapper sysChannelMapper;

    @Resource
    private SysChannelVersionMapper sysChannelVersionMapper;

    @Resource
    private SysPayConfigMapper sysPayConfigMapper;

    @Resource
    private SysBroadcastConverter sysBroadcastConverter;

    @Resource
    private SysBroadcastMapper sysBroadcastMapper;

    public boolean addCardMenu(CardMenuVO cardMenuVO) {
        Long id = redisService.getIncreaseId(RedisTableIdKey.CARD_MENU_ID);
        CardMenuDO cardMenuDO = cardMenuConverter.toDb(cardMenuVO);
        cardMenuDO.setId(id);
        Long now = System.currentTimeMillis();
        cardMenuDO.setCreateTime(now);
        cardMenuDO.setUpdateTime(now);
        cardMenuDO.setIsDel(0);
        cardMenuDO.setPath(buildCardMenuPath(id, cardMenuDO.getParentId()));
        return cardMenuMapper.insert(cardMenuDO) > 0;
    }

    private String buildCardMenuPath(Long selfId, Long parentId) {
        if (parentId == 0) {
            return String.valueOf(selfId);
        }
        CardMenuDO parent = cardMenuMapper.selectById(parentId);
        return parent.getPath() + "/" + selfId;
    }


    public boolean updateCardMenu(CardMenuVO cardMenuVO) {
        CardMenuDO cardMenuDO = cardMenuMapper.selectById(cardMenuVO.getId());
        cardMenuDO.setName(cardMenuVO.getName());
        cardMenuDO.setParentId(cardMenuVO.getParentId());
        cardMenuDO.setMenuType(cardMenuVO.getMenuType());
        cardMenuDO.setSortNum(cardMenuVO.getSortNum());
        cardMenuDO.setUpdateTime(System.currentTimeMillis());
        return cardMenuMapper.updateById(cardMenuDO) > 0;
    }

    public boolean deleteCardMenu(Long id) {
        CardMenuDO cardMenuDO = cardMenuMapper.selectById(id);
        cardMenuDO.setIsDel(1);
        cardMenuDO.setUpdateTime(System.currentTimeMillis());
        return cardMenuMapper.updateById(cardMenuDO) > 0;
    }

    /**
     * 获取卡组菜单
     *
     * @return
     */
    public List<CardMenuVO> getCardMenuVOList(Long examBankId) {
        List<CardMenuDO> cardMenuDOS = cardMenuMapper.getCardMenuList(examBankId);
        List<CardMenuVO> list = cardMenuDOS.stream().map(cardMenuConverter::toView).toList();
        return buildCardMenuVoList(list);
    }

    private List<CardMenuVO> buildCardMenuVoList(List<CardMenuVO> input) {
        Map<Long, CardMenuVO> map = input.stream().collect(Collectors.toMap(CardMenuVO::getId, v -> v));
        List<CardMenuVO> res = new ArrayList<>();
        for (CardMenuVO cardMenuVO : input) {
            // 如果父ID为0，说明是顶层节点，直接添加到结果列表中
            if (cardMenuVO.getParentId() == 0) {
                res.add(cardMenuVO);
            } else {
                CardMenuVO parent = map.get(cardMenuVO.getParentId());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(cardMenuVO);
                }
            }
        }
        return res;
    }

    public List<Long> getSubMenuIds(Long menuId, Long examBankId) {

        List<CardMenuVO> cardMenuVOList = getCardMenuVOList(examBankId);
        List<CardMenuVO> subCardMenu = getSubCardMenu(menuId, cardMenuVOList);
        List<Long> res = new ArrayList<>();
        res.add(menuId);
        doGetSubMenuIds(res, subCardMenu);
        return res;
    }

    // 递归获取子菜单的ID
    public void doGetSubMenuIds(List<Long> res, List<CardMenuVO> input) {
        if (CollectionUtils.isNotEmpty(input)) {
            for (CardMenuVO cardMenuVO : input) {
                res.add(cardMenuVO.getId());
                if (CollectionUtils.isNotEmpty(cardMenuVO.getChildren())) {
                    doGetSubMenuIds(res, cardMenuVO.getChildren());
                }
            }
        }
    }

    public List<CardMenuVO> getSubCardMenu(Long menuId, List<CardMenuVO> input) {
        for (CardMenuVO cardMenuVO : input) {
            if (cardMenuVO.getId().equals(menuId)) {
                return cardMenuVO.getChildren();
            }
            if (CollectionUtils.isNotEmpty(cardMenuVO.getChildren())) {
                // 递归查找
                List<CardMenuVO> tmp = getSubCardMenu(menuId, cardMenuVO.getChildren());
                if (tmp != null) {
                    return tmp;
                }
            }
        }
        return null;
    }


    public PagedData<TagVO> getTagVOList(TagQry qry) {
        List<TagDO> list = tagMapper.selectTagList(qry);
        int total = tagMapper.selectTagCount(qry);
        return PagedData.of(qry, total, list.stream().map(tagConverter::toView).toList());
    }

    public boolean addTag(TagVO tagVO) {
        if (tagMapper.checkTagExist(tagVO.getTagName(), tagVO.getExamBankId()) > 0) {
            throw new RuntimeException("已存在");
        }
        return tagMapper.insert(tagConverter.toDb(tagVO)) > 0;
    }

    public boolean updateTag(TagVO tagVO) {
        return tagMapper.updateById(tagConverter.toDb(tagVO)) > 0;
    }

    public boolean deleteTag(Long id) {
        TagDO tagDO = tagMapper.selectById(id);
        tagDO.setIsDel(1);
        return tagMapper.updateById(tagDO) > 0;
    }

    public PagedData<UserFeedbackVO> getFeedbackList(FeedbackQry qry) {
        List<UserFeedbackDO> list = userFeedbackMapper.selectFeedbackList(qry);
        int count = userFeedbackMapper.selectFeedbackCount(qry);
        return PagedData.of(qry, count, list.stream().map(userFeedbackConverter::toView).toList());
    }

    public boolean handleFeedback(Long id) {
        UserFeedbackDO userFeedbackDO = userFeedbackMapper.selectById(id);
        userFeedbackDO.setIsHandle(1);
        return userFeedbackMapper.updateById(userFeedbackDO) > 0;
    }

    public List<VideoMenuVO> getVideoMenuList(IDQry qry) {
        List<VideoMenuDO> videoMenuList = videoMenuMapper.getVideoMenuList(qry.getId());
        List<VideoMenuVO> res = videoMenuList.stream().map(videoMenuConverter::toView).toList();
        return buildVideMenuList(res);
    }

    private List<VideoMenuVO> buildVideMenuList(List<VideoMenuVO> input) {
        Map<Long, VideoMenuVO> map = input.stream().collect(Collectors.toMap(VideoMenuVO::getId, v -> v));
        List<VideoMenuVO> res = new ArrayList<>();
        for (VideoMenuVO videoMenuVO : input) {
            // 如果父ID为0，说明是顶层节点，直接添加到结果列表中
            if (videoMenuVO.getParentId() == 0) {
                res.add(videoMenuVO);
            } else {
                VideoMenuVO parent = map.get(videoMenuVO.getParentId());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(videoMenuVO);
                }
            }
        }
        return res;
    }

    public boolean addVideoMenu(VideoMenuVO videoMenuVO) {
        Long id = redisService.getIncreaseId(RedisTableIdKey.VIDEO_MENU_ID);
        VideoMenuDO db = videoMenuConverter.toDb(videoMenuVO);
        Long now = System.currentTimeMillis();
        db.setId(id);
        db.setCreateTime(now);
        db.setUpdateTime(now);
        db.setIsDel(0);
        db.setPath(buildVideoMenuPath(id, videoMenuVO.getParentId()));
        return videoMenuMapper.insert(db) > 0;
    }

    private String buildVideoMenuPath(Long selfId, Long parentId) {
        if (parentId == 0) {
            return String.valueOf(selfId);
        }
        VideoMenuDO parent = videoMenuMapper.selectById(parentId);
        return parent.getPath() + "/" + selfId;
    }

    public boolean updateVideoMenu(VideoMenuVO videoMenuVO) {
        VideoMenuDO db = videoMenuConverter.toDb(videoMenuVO);
        db.setUpdateTime(System.currentTimeMillis());
        return videoMenuMapper.updateById(db) > 0;
    }

    public boolean deleteVideoMenu(Long id) {
        VideoMenuDO db = videoMenuMapper.selectById(id);
        db.setIsDel(1);
        db.setUpdateTime(System.currentTimeMillis());
        return videoMenuMapper.updateById(db) > 0;
    }

    public PagedData<VideoMenuIntroVO> getVideoMenuIntroList(VideoMenuIntroQry qry) {
        List<VideoMenuIntroDO> list = videoMenuIntroMapper.getVideoMenuIntroList(qry);
        int count = videoMenuIntroMapper.getVideoMenuIntroCount(qry);
        return PagedData.of(qry, count, list.stream().map(videoMenuIntroConverter::toView).toList());
    }

    public boolean addVideoMenuIntro(VideoMenuIntroVO videoMenuIntroVO) {
        VideoMenuIntroDO db = videoMenuIntroConverter.toDb(videoMenuIntroVO);
        db.setId(IdUtil.getSnowflakeNextId());
        return videoMenuIntroMapper.insert(db) > 0;
    }

    public boolean updateVideoMenuIntro(VideoMenuIntroVO videoMenuIntroVO) {
        return videoMenuIntroMapper.updateById(videoMenuIntroConverter.toDb(videoMenuIntroVO)) > 0;
    }

    public boolean deleteVideoMenuIntro(Long id) {
        return videoMenuIntroMapper.deleteById(id) > 0;
    }

    public List<SysPayConfigVO> getSysPayConfigList() {
        List<SysPayConfigDO> list = sysPayConfigMapper.selectAll();
        return list.stream().map(sysChannelConverter::toPayView).toList();
    }

    public boolean addSysPayConfig(SysPayConfigVO sysPayConfigVO) {
        SysPayConfigDO payDb = sysChannelConverter.toPayDb(sysPayConfigVO);
        Long now = System.currentTimeMillis();
        payDb.setCreateTime(now);
        payDb.setUpdateTime(now);
        return sysPayConfigMapper.insert(payDb) > 0;
    }

    public boolean updateSysPayConfig(SysPayConfigVO sysPayConfigVO) {
        SysPayConfigDO payDb = sysChannelConverter.toPayDb(sysPayConfigVO);
        Long now = System.currentTimeMillis();
        payDb.setUpdateTime(now);
        return sysPayConfigMapper.updateById(payDb) > 0;
    }


    public List<SysChannelVO> getChannelList() {
        List<SysChannelDO> channelDbList = sysChannelMapper.selectAll();
        return channelDbList.stream().map(sysChannelConverter::toChannelView).toList();
    }

    public boolean addChannel(SysChannelVO sysChannelVO) {
        SysChannelDO channelDb = sysChannelConverter.toChannelDb(sysChannelVO);
        Long now = System.currentTimeMillis();
        channelDb.setCreateTime(now);
        channelDb.setUpdateTime(now);
        return sysChannelMapper.insert(channelDb) > 0;
    }

    public boolean updateChannel(SysChannelVO sysChannelVO) {
        SysChannelDO channelDb = sysChannelConverter.toChannelDb(sysChannelVO);
        channelDb.setUpdateTime(System.currentTimeMillis());
        return sysChannelMapper.updateById(channelDb) > 0;
    }

    public PagedData<SysChannelVersionVO> getChannelVersionList(ChannelVersionQry qry) {
        List<SysChannelVersionDO> list = sysChannelVersionMapper.getChannelVersionList(qry);
        int count = sysChannelVersionMapper.getChannelVersionCount(qry);
        return PagedData.of(qry, count, list.stream().map(sysChannelConverter::toVersionView).toList());
    }

    public boolean addChannelVersion(SysChannelVersionVO sysChannelVersionVO) {
        SysChannelVersionDO channelVersionDb = sysChannelConverter.toVersionDb(sysChannelVersionVO);
        Long now = System.currentTimeMillis();
        channelVersionDb.setCreateTime(now);
        channelVersionDb.setUpdateTime(now);
        return sysChannelVersionMapper.insert(channelVersionDb) > 0;
    }

    public boolean updateChannelVersion(SysChannelVersionVO sysChannelVersionVO) {
        SysChannelVersionDO channelVersionDb = sysChannelConverter.toVersionDb(sysChannelVersionVO);
        channelVersionDb.setUpdateTime(System.currentTimeMillis());
        return sysChannelVersionMapper.updateById(channelVersionDb) > 0;
    }

    public void addSysBroadcast(SysBroadcastVO sysBroadcastVO) {
        SysBroadcastDO sysBroadcastDO = sysBroadcastConverter.toDb(sysBroadcastVO);
        sysBroadcastDO.setId(IdUtil.getSnowflakeNextId());
        sysBroadcastDO.setCreateTime(System.currentTimeMillis());
        sysBroadcastMapper.insert(sysBroadcastDO);
    }

    public PagedData<SysBroadcastVO> getSysBroadcastList(BroadcastQry qry) {
        List<SysBroadcastDO> list = sysBroadcastMapper.getSysBroadcastList(qry);
        int total = sysBroadcastMapper.getSysBroadcastCount(qry);
        return PagedData.of(qry, total, list.stream().map(sysBroadcastConverter::toVO).toList());
    }

    public void updateSysBroadcast(SysBroadcastVO sysBroadcastVO) {
        SysBroadcastDO sysBroadcastDO = sysBroadcastConverter.toDb(sysBroadcastVO);
        sysBroadcastMapper.updateById(sysBroadcastDO);
    }

    public CardMenuDO getCardMenuById(Long menuId) {
        return cardMenuMapper.selectById(menuId);
    }
}
